package com.syh.jn.scm.service.impl.supplier.sale;

import cn.redscarf.commons.lock.Lock;
import cn.redscarf.commons.lock.LockParam;
import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.util.bean.ExtBeanUtils;
import cn.redscarf.commons.util.other.ExtAssert;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import com.alibaba.dubbo.config.annotation.Service;
import com.syh.core.ddd.event.annotation.DomainEventHandle;
import com.syh.core.ddd.infrastructure.repository.AggregateRepository;
import com.syh.core.ddd.service.AbstractServiceImpl;
import com.syh.jn.scm.domain.operation.entity.supplier.OperaSupplier;
import com.syh.jn.scm.domain.operation.event.supplier.OperaSupplierEvent;
import com.syh.jn.scm.domain.supplier.entity.finance.OrderExpensesBill;
import com.syh.jn.scm.domain.supplier.entity.finance.StatementAccountBill;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBill;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBillItem;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleOrder;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleOrderItem;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleReturnOrder;
import com.syh.jn.scm.domain.supplier.event.LogisticsBillEvent;
import com.syh.jn.scm.domain.supplier.event.SaleOrderEvent;
import com.syh.jn.scm.domain.supplier.event.SaleReturnOrderEvent;
import com.syh.jn.scm.domain.supplier.event.finance.BusinessAccountRecordEvent;
import com.syh.jn.scm.domain.supplier.event.finance.OrderExpensesBillEvent;
import com.syh.jn.scm.domain.supplier.event.finance.StatementAccountBillEvent;
import com.syh.jn.scm.domain.supplier.repository.logistics.LogisticsBillReadRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleOrderRepository;
import com.syh.jn.scm.dto.supplier.sale.*;
import com.syh.jn.scm.dto.support.FilesDto;
import com.syh.jn.scm.service.api.supplier.finance.enums.OrderExpensesBillEnums;
import com.syh.jn.scm.service.api.supplier.finance.enums.StatementAccountBillEnum;
import com.syh.jn.scm.service.api.supplier.logistics.enums.LogisticsBillEnums;
import com.syh.jn.scm.service.api.supplier.sale.SaleOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * Date:2018/7/16
 * Author:Clown
 * Desc;
 */
@Service
@Transactional
public class SaleOrderServiceImpl extends AbstractServiceImpl<SaleOrderSto, SaleOrderMto, SaleOrderVo, SaleOrder, Long> implements SaleOrderService {
  @Autowired
  private SaleOrderRepository saleOrderRepository;
  @Resource(name = "saleOrderReadRepositoryMbImpl")
  private SaleOrderReadRepository saleOrderReadRepositoryMyImpl;

  @Override
  protected AggregateRepository<SaleOrder, Long> getAggregateRepository() {
    //TODO 继承AbstractServiceImpl类是为了抛出事件
    return null;
  }

  @Override
  public SaleOrderVo getById(Long id) {
    return this.saleOrderReadRepositoryMyImpl.getById(id);
  }

  @Override
  public boolean contains(Long id) {
    return this.saleOrderReadRepositoryMyImpl.contains(id);
  }

  @Override
  public SaleOrderVo getOne(Wrapper wrapper) {
    return this.saleOrderReadRepositoryMyImpl.getOne(wrapper);
  }

  @Override
  public List<SaleOrderVo> findList(Wrapper wrapper) {
    return this.saleOrderReadRepositoryMyImpl.getList(wrapper);
  }

  @Override
  public List<SaleOrderVo> findListPage(Wrapper wrapper, Integer startRow, Integer count) {
    return this.saleOrderReadRepositoryMyImpl.findListPage(wrapper, startRow, count);
  }

  @Override
  public int getCount(Wrapper wrapper) {
    return this.saleOrderRepository.getCount(wrapper);
  }

  @Override
  public int getCountView(Wrapper wrapper) {
    return this.saleOrderReadRepositoryMyImpl.getCount(wrapper);
  }

  /**
   * 新增销售单
   *
   * @param sto
   * @return
   */
  @Override
  public Long save(SaleOrderSto sto) {
    SaleOrder saleOrder = SaleOrder.created(sto);
    Long orderId = saleOrderRepository.add(saleOrder);
//    this.publicEvent(SaleOrderEvent.buildSaleOrderCreated(new Object[]{saleOrder}));
    return orderId;
  }

  /**
   * 修改销售单
   *
   * @param mto
   */
  @Lock(lockNamePre = "supplier_saleOrder")
  @Override
  public void update(@LockParam(filed = "id") SaleOrderMto mto) {
    SaleOrder order = judgeNull(mto.getId());//判空
    order.modify(mto);
    saleOrderRepository.update(order);
  }


  /**
   * 根据id删除订单
   *
   * @param id
   */
  @Override
  @Lock(lockNamePre = "supplier_saleOrder")
  public void delete(@LockParam Long id) {
    SaleOrder order = judgeNull(id);//判空
    order.destroy();
    saleOrderRepository.remove(order);
    this.publicEvent(SaleOrderEvent.buildSaleOrderDestory(new Object[]{order}));
  }

  /**
   * 根据id确认订单
   *
   * @param id
   */
  @Lock(lockNamePre = "supplier_saleOrder")
  @Override
  public void confirm(@LockParam Long id) {
    SaleOrder order = judgeNull(id);
    order.confirm();
    saleOrderRepository.updateStatus(order);
  }

  /**
   * 反确认订单
   *
   * @param id
   */
  @Lock
  @Override
  public void antiConfirm(@LockParam Long id) {
    SaleOrder order = judgeNull(id);
    order.cancelConfirm();
    saleOrderRepository.updateStatus(order);
    this.publicEvent(SaleOrderEvent.buildSaleOrderCancelConfirm(new Object[]{order}));
  }

  /**
   * 取消订单
   *
   * @param id
   */
  @Lock(lockNamePre = "supplier_saleOrder")
  @Override
  public void cancel(@LockParam Long id) {
    SaleOrder order = judgeNull(id);
    order.cancel();
    saleOrderRepository.updateStatus(order);
    this.publicEvent(SaleOrderEvent.buildSaleOrderCancel(new Object[]{order}));
  }

  /**
   * 上传单据图片
   *
   * @param dto
   */
  @Override
  public void upload(FilesDto dto) {
    SaleOrder order = judgeNull(dto.getId());
    order.upload(dto);
    saleOrderRepository.upload(order);
  }

  /**
   * 批量新增订单
   *
   * @param stoList
   * @return
   */
  @Override
  public List<Long> batchSave(List<SaleOrderSto> stoList) {
    List<Long> ids = new ArrayList<>();
    stoList.stream().forEach(sto -> {
      Long id = this.save(sto);
      ids.add(id);
    });
    return ids;
  }

  /**
   * 销售订单统计金额
   *
   * @param wrapper
   * @return
   */
  @Override
  public SaleStatisticalAmountVo statisticalAmount(Wrapper wrapper) {
    return this.saleOrderReadRepositoryMyImpl.statisticalAmount(wrapper);
  }

  @Override
  public SaleStatisticalAmountVo statisticalAmountByOrder(Wrapper we) {
    return this.saleOrderReadRepositoryMyImpl.statisticalAmountByOrder(we);
  }

  @Override
  public List<SaleStatisticalAmountVo> statisticalAmountByCustomer(Wrapper we) {
    return this.saleOrderReadRepositoryMyImpl.statisticalAmountByCustomer(we);
  }

  @Override
  public List<SaleStatisticalAmountVo> statisticalAmountByGoods(Wrapper we) {
    return this.saleOrderReadRepositoryMyImpl.statisticalAmountByGoods(we);
  }

  /**
   * 根据id查询商品明细方法
   *
   * @param id
   * @return
   */
  @Override
  public List<SaleOrderItemVo> getItemsBySaleOrderId(Long id) {
//    return this.transformSale(saleOrderRepository.getItemsBySaleOrderId(id));
    return this.saleOrderReadRepositoryMyImpl.getItemsBySaleOrderId(id);
  }

  /**
   * 根据ids获取明细
   *
   * @param ids
   * @return
   */
  @Override
  public List<SaleOrderItemVo> getItemsBySaleOrderIds(Long[] ids) {
//    return this.transformSale(saleOrderRepository.getItemsBySaleOrderIds(ids));
    return this.saleOrderReadRepositoryMyImpl.getItemsBySaleOrderIds(ids);
  }

  /**
   * 根据商品id明细商品
   *
   * @param goodsId
   * @return
   */
  @Override
  public SaleOrderItemVo getByGoodsId(Long goodsId, Long id) {
    return this.saleOrderReadRepositoryMyImpl.getByGoodsId(goodsId, id);
  }


  /**
   * 判空方法
   *
   * @param id
   * @return
   */
  private SaleOrder judgeNull(Long id) {
    ExtAssert.notNull(id, "id不能为空");
    SaleOrder entity = saleOrderRepository.getById(id);
    ExtAssert.notNull(entity, "订单不存在");
    return entity;
  }

  /**
   * 消费者：监听对账单（生成对账单状态和对账单号）
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_CREATED)
  protected void handleStatementBillAccountCreated(StatementAccountBill bill) {
    bill.getStatementAccountBillItems().forEach(billItem -> {
      if (billItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_SALE_ORD.getStatus())) {
        SaleOrder saleOrder = saleOrderRepository.getById(billItem.getSourceId());
        saleOrder.generating(bill.getId());
        saleOrderRepository.updateStatus(saleOrder);
      }
    });
  }

  /**
   * 消费者：监听物流单创建（记录生成物流商品数量）
   *
   * @param bill
   */
  @DomainEventHandle(LogisticsBillEvent.LOGISTICS_BILL_CREATED)
  protected void handleLogisticsBillCreated(LogisticsBill bill) {
    if (LogisticsBillEnums.OrderType.SALE.getStatus().equals(bill.getOrderType())) {
      SaleOrder order = saleOrderRepository.getById(bill.getOrderId());
      order.countGeneratedQuantity(bill, 1);
      saleOrderRepository.updateOrderAndItems(order);
    }
  }

  /**
   * 消费者：监听物流单删除（记录生成物流商品数量）
   *
   * @param bill
   */
  @DomainEventHandle(LogisticsBillEvent.LOGISTICS_BILL_DELETED)
  protected void handleLogisticsBillDeleted(LogisticsBill bill) {
    if (LogisticsBillEnums.OrderType.SALE.getStatus().equals(bill.getOrderType())) {
      SaleOrder order = saleOrderRepository.getById(bill.getOrderId());
      order.countGeneratedQuantity(bill, 2);
      saleOrderRepository.updateOrderAndItems(order);
    }
  }

  /**
   * 消费者：监听物流单发货（修改发货数量）
   *
   * @param bill
   */
  @DomainEventHandle(BusinessAccountRecordEvent.BUSINESS_ACCOUNT_RECORD_CHECK_DISCOUNT_AFTER)
  protected void handleLogisticsBillDeliver(LogisticsBill bill) {
    if (LogisticsBillEnums.OrderType.SALE.getStatus().equals(bill.getOrderType())) {
      SaleOrder order = saleOrderRepository.getById(bill.getOrderId());
      order.delivery(bill);
      saleOrderRepository.updateOrderAndItems(order);
    }
  }

  /**
   * 消费者：监听物流单取消事件（修改发货数量）
   *
   * @param bill
   */
  @DomainEventHandle(BusinessAccountRecordEvent.BUSINESS_ACCOUNT_RECORD_CHECK_DISCOUNT_BEFORE)
  protected void handleLogisticsBillCancelled(LogisticsBill bill) {
    if (LogisticsBillEnums.OrderType.SALE.getStatus().equals(bill.getOrderType())) {
      SaleOrder order = saleOrderRepository.getById(bill.getOrderId());
      order.cancelDelivery(bill);
      saleOrderRepository.updateOrderAndItems(order);
    }
  }

  /**
   * 消费者：监听订单费用表创建事件或保存（记录费用表id）
   *
   * @param orderExpensesBill
   */
  @DomainEventHandle({OrderExpensesBillEvent.ORDER_EXPENSES_BILL_CREATED, OrderExpensesBillEvent.ORDER_EXPENSES_BILL_UPDATED})
  protected void handleOrderExpensesBillCreatedOrUpdated(OrderExpensesBill orderExpensesBill) {
    if (orderExpensesBill.getOrderType().equals(OrderExpensesBillEnums.OrderType.SALE_ORDER.getStatus())) {
      SaleOrder saleOrder = this.saleOrderRepository.getById(orderExpensesBill.getSourceId());
      saleOrder.addExpensesBill(orderExpensesBill.getId());
      this.saleOrderRepository.updateStatus(saleOrder);
    }
  }

  /**
   * 消费者：监听订单费用表删除事件（删除订单费用信息）
   *
   * @param orderExpensesBill
   */
  @DomainEventHandle(OrderExpensesBillEvent.ORDER_EXPENSES_BILL_DELETED)
  protected void handleOrderExpensesBillDeleted(OrderExpensesBill orderExpensesBill) {
    if (orderExpensesBill.getOrderType().equals(OrderExpensesBillEnums.OrderType.SALE_ORDER.getStatus())) {
      SaleOrder saleOrder = this.saleOrderRepository.getById(orderExpensesBill.getSourceId());
      saleOrder.deletedExpensesBill(orderExpensesBill.getId());
      this.saleOrderRepository.updateStatus(saleOrder);
    }
  }

  /**
   * 消费者：监听费用表确认事件（计算订单费用总额）
   *
   * @param orderExpensesBill
   */
  @DomainEventHandle(OrderExpensesBillEvent.ORDER_EXPENSES_BILL_CONFIRM)
  protected void handleOrderExpensesBillConfirm(OrderExpensesBill orderExpensesBill) {
    if (orderExpensesBill.getOrderType().equals(OrderExpensesBillEnums.OrderType.SALE_ORDER.getStatus())) {
      SaleOrder saleOrder = this.saleOrderRepository.getById(orderExpensesBill.getSourceId());
      saleOrder.confirmExpensesBill(orderExpensesBill);
      this.saleOrderRepository.updateStatus(saleOrder);
    }
  }

  /**
   * 消费者：监听费用表取消事件（计算订单费用总额）
   *
   * @param orderExpensesBill
   * @param oldBill
   */
  @DomainEventHandle(OrderExpensesBillEvent.ORDER_EXPENSES_BILL_CANCEL)
  protected void handleOrderExpensesBillCancel(OrderExpensesBill orderExpensesBill, OrderExpensesBill oldBill) {
    if (orderExpensesBill.getOrderType().equals(OrderExpensesBillEnums.OrderType.SALE_ORDER.getStatus())) {
      SaleOrder saleOrder = this.saleOrderRepository.getById(orderExpensesBill.getSourceId());
      saleOrder.cancelExpensesBill(orderExpensesBill);
      this.saleOrderRepository.updateStatus(saleOrder);
    }
  }

  /**
   * 消费者：监听销售退货单创建（记录已生成退货数量）
   *
   * @param returnOrder
   */
  @DomainEventHandle(SaleReturnOrderEvent.SALE_RETURN_ORDER_CREATED)
  protected void handleSaleReturnOrderCreated(SaleReturnOrder returnOrder) {
    SaleOrder order = saleOrderRepository.getById(returnOrder.getSaleOrderId());
    order.createdReturnOrder(returnOrder);
    saleOrderRepository.updateOrderAndItems(order);
  }

  /**
   * 消费者：监听销售退货单修改（记录已生成退货数量）
   *
   * @param returnOrder
   */
  @DomainEventHandle(SaleReturnOrderEvent.SALE_RETURN_ORDER_UPDATED)
  protected void handleSaleReturnOrderUpdated(SaleReturnOrder returnOrder, SaleReturnOrder oldReturnOrder) {
    SaleOrder order = saleOrderRepository.getById(returnOrder.getSaleOrderId());
    order.updatedReturnOrder(returnOrder, oldReturnOrder);
    saleOrderRepository.updateOrderAndItems(order);
  }

  /**
   * 消费者：监听销售退货单取消（记录已生成退货数量）
   *
   * @param returnOrder
   */
  @DomainEventHandle(SaleReturnOrderEvent.SALE_RETURN_ORDER_CANCEL)
  protected void handleSaleReturnOrderCancel(SaleReturnOrder returnOrder) {
    SaleOrder order = saleOrderRepository.getById(returnOrder.getSaleOrderId());
    order.cancelReturnOrder(returnOrder);
    saleOrderRepository.updateOrderAndItems(order);
  }

  /**
   * 消费者：监听对账单（修改对账单状态）
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_UPDATA)
  protected void handleStatementBillAccountUpdata(StatementAccountBill bill) {
    Wrapper wrapper = Wrapper.build();
    wrapper.append(Restrictions.eq("statementId", bill.getId()));
    List<SaleOrder> saleOrders = saleOrderRepository.getList(wrapper);
    saleOrders.forEach(saleOrder -> {
      saleOrder.cancelGenerating();
      saleOrderRepository.updateStatus(saleOrder);
    });
    bill.getStatementAccountBillItems().forEach(billItem -> {
      if (billItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_SALE_ORD.getStatus())) {
        SaleOrder saleOrder = saleOrderRepository.getById(billItem.getSourceId());
        saleOrder.generating(bill.getId());
        saleOrderRepository.updateStatus(saleOrder);
      }
    });
  }

  /**
   * 消费者：监听对账单（删除对账单状态和对账单号）
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_DESTROY)
  protected void handleStatementBillAccountDestroy(StatementAccountBill bill) {
    bill.getStatementAccountBillItems().forEach(billItem -> {
      if (billItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_SALE_ORD.getStatus())) {
        SaleOrder saleOrder = saleOrderRepository.getById(billItem.getSourceId());
        saleOrder.cancelGenerating();
        saleOrderRepository.updateStatus(saleOrder);
      }
    });
  }

  /**
   * 消费者：监听服务商反开账（删除所有该服务商的销售单）
   */
  @DomainEventHandle(OperaSupplierEvent.SUPPLIER_REVERSAL_ACCOUNT)
  protected void handleSupplierReversalAccount(OperaSupplier supplier) {
    this.saleOrderRepository.deleteByBusinessEntityId(supplier.getId());
  }

  /**
   * 纠正订单物流相关字段数据
   */
  @Override
  public void correctingLogisticsData() {
    //获取所有销售订单
    Wrapper we = Wrapper.build();
    List<SaleOrder> saleOrders = saleOrderRepository.getList(we);
    Long[] saleOrderIds = new Long[saleOrders.size()];
    //获取所有未发货的销售物流订单
    LogisticsBillReadRepository logisticsBillReadRepository = (LogisticsBillReadRepository) SpringContextUtils.getBean(LogisticsBillReadRepository.class);
    Wrapper logisticsWe = Wrapper.build().append(Restrictions.eq("orderType", LogisticsBillEnums.OrderType.SALE.getStatus()))
            .append(Restrictions.eq("logisticsState", LogisticsBillEnums.State.UNDELIVERY.getStatus()));
    List<LogisticsBill> logisticsBills = logisticsBillReadRepository.getList(logisticsWe);

    int index = 0;
    //获取销售订单id数组
    for (SaleOrder saleorder : saleOrders) {
      saleOrderIds[index++] = saleorder.getId();
      saleorder.setItems(new ArrayList<SaleOrderItem>());
    }
    //根据销售订单id数组获取销售订单项
    List<SaleOrderItem> saleOrderItems = saleOrderRepository.getItemsBySaleOrderIds(saleOrderIds);
    Long[] logisticsBillIds = new Long[logisticsBills.size()];
    index = 0;
    for (LogisticsBill logisticsBill : logisticsBills) {
      logisticsBillIds[index++] = logisticsBill.getId();
      logisticsBill.setItems(new ArrayList<LogisticsBillItem>());
    }
    //根据物流单id数组获取物流单项
    List<LogisticsBillItem> logisticsBillItems = logisticsBillIds.length == 0 ? new ArrayList<>() : logisticsBillReadRepository.getItemsByLogisticsBillIds(logisticsBillIds);
    for (LogisticsBill logisticsBill : logisticsBills) {
      //匹配物流单
      logisticsBillItems.forEach(logisticsBillItem -> {
        if (logisticsBillItem.getLogisticsBillId().equals(logisticsBill.getId())) {
          logisticsBill.getItems().add(logisticsBillItem);
        }
      });
    }
    for (SaleOrder saleorder : saleOrders) {
      //匹配销售订单项
      saleOrderItems.forEach(saleOrderItem -> {
        if (saleOrderItem.getSaleOrderId().equals(saleorder.getId())) {
          saleorder.getItems().add(saleOrderItem);
        }
      });
      //销售单相关物流单
      List<LogisticsBillItem> saleLogisticsBillItems = new ArrayList<>();
      logisticsBills.forEach(logisticsBill -> {
        if (logisticsBill.getOrderId().equals(saleorder.getId())) {
          saleLogisticsBillItems.addAll(logisticsBill.getItems());
        }
      });
      saleorder.correctingLogisticsData(saleLogisticsBillItems);
      saleOrderRepository.updateOrderAndItems(saleorder);
    }
  }

  /**
   * 内部方法  领域对象转vo
   *
   * @param entityList
   * @return
   */
  private List<SaleOrderItemVo> transformSale(List<SaleOrderItem> entityList) {
    List<SaleOrderItemVo> voList = new ArrayList<>();
    entityList.stream().forEach((entity) -> {
      SaleOrderItemVo vo = new SaleOrderItemVo();
      ExtBeanUtils.copyProperties(entity, vo);
      voList.add(vo);
    });
    return voList;
  }

  private SaleOrderItemVo trans(SaleOrderItem entity) {
    SaleOrderItemVo vo = new SaleOrderItemVo();
    ExtBeanUtils.copyProperties(entity, vo);
    return vo;
  }
}
